Hệ thống quản lý phòng khám trực tuyến bằng PHP

1 <?php
2     
if(!defined('datalist_db_encoding')) define('datalist_db_encoding', 'iso-8859-1');
3     
if(function_exists('date_default_timezone_set')) @date_default_timezone_set('America/New_York');
4
5     
/* force caching */
6     $last_modified = filemtime(__FILE__);
7     $last_modified_gmt = gmdate(
'D, d M Y H:i:s', $last_modified) . ' GMT';
8     $headers = (function_exists(
'getallheaders') ? getallheaders() : $_SERVER);
9     
if(isset($headers['If-Modified-Since']) && (strtotime($headers['If-Modified-Since']) == $last_modified)){
10         @header(
"Last-Modified: {$last_modified_gmt}", true, 304);
11         @header(
"Cache-Control: public, max-age=240", true);
12         exit;
13     }
14
15     @header(
"Last-Modified: {$last_modified_gmt}", true, 200);
16     @header(
"Cache-Control: public, max-age=240", true);
17     @header(
'Content-Type: text/javascript; charset=' . datalist_db_encoding);
18     $currDir = dirname(__FILE__);
19     include(
"{$currDir}/defaultLang.php");
20     include(
"{$currDir}/language.php");
21 ?>

22 var
AppGini = AppGini || {};
23 AppGini.ajaxCache = function(){
24     
var _tests = [];
25
26     
/*
27         An array of functions that receive a parameterless url and a parameters
object,
28         makes a test,
29         and
if test passes, executes something and/or
30         returns a non-
false value if test passes,
31         or
false if test failed (useful to tell if tests should continue or not)
32     */

33     
var addCheck = function(check){ //
34         
if(typeof(check) == 'function'){
35             _tests.push(check);
36         }
37     };
38
39     
var _jqAjaxData = function(opt){ //
40         
var opt = opt || {};
41         
var url = opt.url || '';
42         
var data = opt.data || {};
43
44         
var params = url.match(/\?(.*)$/);
45         
var param = (params !== null ? params[1] : '');
46
47         
var sPageURL = decodeURIComponent(param),
48             sURLVariables = sPageURL.split(
'&'),
49             sParameter,
50             i;
51
52         
for(i = 0; i < sURLVariables.length; i++){
53             sParameter = sURLVariables[i].split(
'=');
54             
if(sParameter[0] == '') continue;
55             data[sParameter[
0]] = sParameter[1] || '';
56         }
57
58         
return data;
59     };
60
61     
var start = function(){ //
62         
if(!_tests.length) return; // no need to monitor ajax requests since no checks were defined
63         
var reqTests = _tests;
64         $j.ajaxPrefilter(function(options, originalOptions, jqXHR){
65             
var success = originalOptions.success || $j.noop,
66                 data = _jqAjaxData(originalOptions),
67                 oUrl = originalOptions.url ||
'',
68                 url = oUrl.match(/\?/) ? oUrl.match(/(.*)\?/)[
1] : oUrl;
69
70             options.beforeSend = function(){
//
71                 
var req, cached = false, resp;
72
73                 
for(var i = 0; i < reqTests.length; i++){
74                     resp = reqTests[i](url, data);
75                     
if(resp === false) continue;
76
77                     success(resp);
78                     
return false;
79                 }
80
81                 
return true;
82             }
83         });
84     };
85
86     
return {
87         addCheck: addCheck,
88         start: start
89     };
90 };

91
92 /* initials and fixes */

93 jQuery(function(){
94     AppGini.count_ajaxes_blocking_saving =
0;
95
96     
/* add ":truncated" pseudo-class to detect elements with clipped text */
97     $j.expr[
':'].truncated = function(obj){
98         
var $this = $j(obj);
99         
var $c = $this
100                     .clone()
101                     .css({ display:
'inline', width: 'auto', visibility: 'hidden', 'padding-right': 0 })
102                     .css({
'font-size': $this.css('font-size') })
103                     .appendTo(
'body');
104
105         
var e_width = $this.outerWidth();
106         
var c_width = $c.outerWidth();
107         $c.
remove();
108
109         
return ( c_width > e_width );
110     };
111
112     $j(window).resize(function(){
113         
var window_width = $j(window).width();
114         
var max_width = $j('body').width() * 0.5;
115
116         
if($j('fieldset .col-xs-11').length) max_width = $j('fieldset .col-xs-11').width() - select2_max_width_decrement();
117         $j(
'.select2-container:not(.option_list)').css({ 'max-width' : max_width + 'px', 'width': '100%' });
118         fix_table_responsive_width();
119
120         
var full_img_factor = 0.9; /* xs */
121         
if(window_width >= 992) full_img_factor = 0.6; /* md, lg */
122         
else if(window_width >= 768) full_img_factor = 0.9; /* sm */
123
124         $j(
'.detail_view .img-responsive').css({'max-width' : parseInt($j('.detail_view').width() * full_img_factor) + 'px'});
125
126         
/* remove labels from truncated buttons, leaving only glyphicons */
127         $j(
'.btn:truncated').each(function(){
128             // hide text
129             
var label = $j(this).html();
130             
var mlabel = label.replace(/.*(<i.*?><\/i>).*/, '$1');
131             $j(
this).html(mlabel);
132         });
133     });
134
135     setTimeout(function(){ $j(window).resize(); },
1000);
136     setTimeout(function(){ $j(window).resize(); },
3000);
137
138     
/* don't allow saving detail view when there's an ajax request to a url that matches the following */
139     
var ajax_blockers = new RegExp(/(ajax_combo\.php|_autofill\.php|ajax_check_unique\.php)/);
140     $j(document).ajaxSend(function(e, r, s){
141         
if(s.url.match(ajax_blockers)){
142             AppGini.count_ajaxes_blocking_saving++;
143             $j(
'#update, #insert').prop('disabled', true);
144         }
145     });
146     $j(document).ajaxComplete(function(e, r, s){
147         
if(s.url.match(ajax_blockers)){
148             AppGini.count_ajaxes_blocking_saving = Math.max(AppGini.count_ajaxes_blocking_saving -
1, 0);
149             
if(AppGini.count_ajaxes_blocking_saving <= 0)
150                 $j(
'#update, #insert').prop('disabled', false);
151         }
152     });
153
154     
/* don't allow responsive images to initially exceed the smaller of their actual dimensions, or .6 container width */
155     jQuery(
'.detail_view .img-responsive').each(function(){
156          
var pic_real_width, pic_real_height;
157          
var img = jQuery(this);
158          jQuery(
'<img/>') // Make in memory copy of image to avoid css issues
159                 .attr(
'src', img.attr('src'))
160                 .load(function() {
161                     pic_real_width =
this.width;
162                     pic_real_height =
this.height;
163
164                     
if(pic_real_width > $j('.detail_view').width() * .6) pic_real_width = $j('.detail_view').width() * .6;
165                     img.css({
"max-width": pic_real_width });
166                 });
167     });
168
169     jQuery(
'.table-responsive .img-responsive').each(function(){
170          
var pic_real_width, pic_real_height;
171          
var img = jQuery(this);
172          jQuery(
'<img/>') // Make in memory copy of image to avoid css issues
173                 .attr(
'src', img.attr('src'))
174                 .load(function() {
175                     pic_real_width =
this.width;
176                     pic_real_height =
this.height;
177
178                     
if(pic_real_width > $j('.table-responsive').width() * .6) pic_real_width = $j('.table-responsive').width() * .6;
179                     img.css({
"max-width": pic_real_width });
180                 });
181     });
182
183     
/* toggle TV action buttons based on selected records */
184     jQuery(
'.record_selector').click(function(){
185         
var id = jQuery(this).val();
186         
var checked = jQuery(this).prop('checked');
187         update_action_buttons();
188     });
189
190     
/* select/deselect all records in TV */
191     jQuery(
'#select_all_records').click(function(){
192         jQuery(
'.record_selector').prop('checked', jQuery(this).prop('checked'));
193         update_action_buttons();
194     });
195
196     
/* fix behavior of select2 in bootstrap modal. See: https://github.com/ivaynberg/select2/issues/1436 */
197     jQuery.fn.modal.Constructor.prototype.enforceFocus = function(){
/**/ };
198
199     
/* remove empty navbar menus */
200     $j(
'nav li.dropdown').each(function(){
201         
var num_items = $j(this).children('.dropdown-menu').children('li').length;
202         
if(!num_items) $j(this).remove();
203     })
204
205     update_action_buttons();
206
207     
/* remove empty images and links from TV, TVP */
208     $j(
'.table a[href="<?php echo $Translation['ImageFolder']; ?>"], .table img[src="<?php echo $Translation['ImageFolder']; ?>"]').remove();
209
210     
/* remove empty email links from TV, TVP */
211     $j(
'a[href="mailto:"]').remove();
212 });

213
214 /* show/hide TV action buttons based
on whether records are selected or not */
215 function update_action_buttons(){
216     
if(jQuery('.record_selector:checked').length){
217         jQuery(
'.selected_records').removeClass('hidden');
218         jQuery(
'#select_all_records')
219             .prop(
'checked', (jQuery('.record_selector:checked').length == jQuery('.record_selector').length));
220     }
else{
221         jQuery(
'.selected_records').addClass('hidden');
222     }
223 }

224
225 /* fix table-responsive behavior
on Chrome */
226 function fix_table_responsive_width(){
227     
var resp_width = jQuery('div.table-responsive').width();
228     
var table_width;
229
230     
if(resp_width){
231         jQuery(
'div.table-responsive table').width('100%');
232         table_width = jQuery(
'div.table-responsive table').width();
233         resp_width = jQuery(
'div.table-responsive').width();
234         
if(resp_width == table_width){
235             jQuery(
'div.table-responsive table').width(resp_width - 1);
236         }
237     }
238 }
239
240 function patients_validateData(){
241     $j(
'.has-error').removeClass('has-error');
242     
if($j('#last_name').val() == ''){ modal_window({ message: '<div class="alert alert-danger"><?php echo addslashes($Translation['field not null']); ?></div>', title: "<?php echo addslashes($Translation['error:']); ?> Last name", close: function(){ $j('[name=last_name]').focus(); $j('[name=last_name]').parents('.form-group').addClass('has-error'); } }); return false; };
243     
if($j('#first_name').val() == ''){ modal_window({ message: '<div class="alert alert-danger"><?php echo addslashes($Translation['field not null']); ?></div>', title: "<?php echo addslashes($Translation['error:']); ?> First name", close: function(){ $j('[name=first_name]').focus(); $j('[name=first_name]').parents('.form-group').addClass('has-error'); } }); return false; };
244     
if($j('#gender').val() == ''){ modal_window({ message: '<div class="alert alert-danger"><?php echo addslashes($Translation['field not null']); ?></div>', title: "<?php echo addslashes($Translation['error:']); ?> Gender", close: function(){ $j('[name=gender]').focus(); $j('[name=gender]').parents('.form-group').addClass('has-error'); } }); return false; };
245     
if($j('#sexual_orientation').val() == ''){ modal_window({ message: '<div class="alert alert-danger"><?php echo addslashes($Translation['field not null']); ?></div>', title: "<?php echo addslashes($Translation['error:']); ?> Sexual orientation", close: function(){ $j('[name=sexual_orientation]').focus(); $j('[name=sexual_orientation]').parents('.form-group').addClass('has-error'); } }); return false; };
246     
if($j('#tobacco_usage').val() == ''){ modal_window({ message: '<div class="alert alert-danger"><?php echo addslashes($Translation['field not null']); ?></div>', title: "<?php echo addslashes($Translation['error:']); ?> Tobacco usage", close: function(){ $j('[name=tobacco_usage]').focus(); $j('[name=tobacco_usage]').parents('.form-group').addClass('has-error'); } }); return false; };
247     
if($j('#alcohol_intake').val() == ''){ modal_window({ message: '<div class="alert alert-danger"><?php echo addslashes($Translation['field not null']); ?></div>', title: "<?php echo addslashes($Translation['error:']); ?> Alcohol Intake", close: function(){ $j('[name=alcohol_intake]').focus(); $j('[name=alcohol_intake]').parents('.form-group').addClass('has-error'); } }); return false; };
248     
if($j('#history').val() == ''){ modal_window({ message: '<div class="alert alert-danger"><?php echo addslashes($Translation['field not null']); ?></div>', title: "<?php echo addslashes($Translation['error:']); ?> History", close: function(){ $j('[name=history]').focus(); $j('[name=history]').parents('.form-group').addClass('has-error'); } }); return false; };
249     
return true;
250 }
251 function disease_symptoms_validateData(){
252     $j(
'.has-error').removeClass('has-error');
253     
return true;
254 }
255 function medical_records_validateData(){
256     $j(
'.has-error').removeClass('has-error');
257     
return true;
258 }
259 function events_validateData(){
260     $j(
'.has-error').removeClass('has-error');
261     
if(!$j('[name=status]:checked').length){ modal_window({ message: '<div class="alert alert-danger"><?php echo addslashes($Translation['field not null']); ?></div>', title: "<?php echo addslashes($Translation['error:']); ?> Status", close: function(){ $j('[name=status]').focus(); $j('[name=status]').parents('.form-group').addClass('has-error'); } }); return false; };
262     
return true;
263 }
264
265 function post(url,
params, update, disable, loading, success_callback){
266     $j.ajax({
267         url: url,
268         type:
'POST',
269         data:
params,
270         beforeSend: function() {
271             
if($j('#' + disable).length) $j('#' + disable).prop('disabled', true);
272             
if($j('#' + loading).length && update != loading) $j('#' + loading).html('<div style="direction: ltr;"><img src="loading.gif"> <?php echo addslashes($Translation['Loading ...']); ?></div>');
273         },
274         success: function(resp) {
275             
if($j('#' + update).length) $j('#' + update).html(resp);
276             
if(success_callback != undefined) success_callback();
277         },
278         complete: function() {
279             
if($j('#' + disable).length) $j('#' + disable).prop('disabled', false);
280             
if($j('#' + loading).length && loading != update) $j('#' + loading).html('');
281         }
282     });
283 }
284
285 function post2(url,
params, notify, disable, loading, redirectOnSuccess){
286     
new Ajax.Request(
287         url, {
288             method:
'post',
289             parameters:
params,
290             onCreate: function() {
291                 
if($(disable) != undefined) $(disable).disabled=true;
292                 
if($(loading) != undefined) $(loading).show();
293             },
294             onSuccess: function(resp) {
295                 
/* show notification containing returned text */
296                 
if($(notify) != undefined) $(notify).removeClassName('Error').appear().update(resp.responseText);
297
298                 
/* in case no errors returned, */
299                 
if(!resp.responseText.match(/<?php echo $Translation['error:']; ?>/)){
300                     
/* redirect to provided url */
301                     
if(redirectOnSuccess != undefined){
302                         window.location=redirectOnSuccess;
303
304                     
/* or hide notification after a few seconds if no url is provided */
305                     }
else{
306                         
if($(notify) != undefined) window.setTimeout(function(){ $(notify).fade(); }, 15000);
307                     }
308
309                 
/* in case of error, apply error class */
310                 }
else{
311                     $(notify).addClassName(
'Error');
312                 }
313             },
314             onComplete: function() {
315                 
if($(disable) != undefined) $(disable).disabled=false;
316                 
if($(loading) != undefined) $(loading).hide();
317             }
318         }
319     );
320 }
321 function passwordStrength(password, username){
322     
// score calculation (out of 10)
323     
var score = 0;
324     re =
new RegExp(username, 'i');
325     
if(username.length && password.match(re)) score -= 5;
326     
if(password.length < 6) score -= 3;
327     
else if(password.length > 8) score += 5;
328     
else score += 3;
329     
if(password.match(/(.*[0-9].*[0-9].*[0-9])/)) score += 3;
330     
if(password.match(/(.*[!,@,#,$,%,^,&,*,?,_,~].*[!,@,#,$,%,^,&,*,?,_,~])/)) score += 5;
331     
if(password.match(/([a-z].*[A-Z])|([A-Z].*[a-z])/)) score += 2;
332
333     
if(score >= 9)
334         
return 'strong';
335     
else if(score >= 5)
336         
return 'good';
337     
else
338         
return 'weak';
339 }
340 function validateEmail(email) {
341     
var re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
342     
return re.test(email);
343 }
344 function loadScript(jsUrl, cssUrl, callback){
345     
// adding the script tag to the head
346     
var head = document.getElementsByTagName('head')[0];
347     
var script = document.createElement('script');
348     script.type = 'text/javascript';
349     script.src = jsUrl;
350
351     
if(cssUrl != ''){
352         
var css = document.createElement('link');
353         css.href = cssUrl;
354         css.rel = "
stylesheet";
355         css.type = "
text/css";
356         head.appendChild(css);
357     }
358
359     
// then bind the event to the callback function
360     
// there are several events for cross browser compatibility
361     
if(script.onreadystatechange != undefined){ script.onreadystatechange = callback; }
362     
if(script.onload != undefined){ script.onload = callback; }
363
364     
// fire the loading
365     head.appendChild(script);
366 }

367 /**
368  * options
object. The following members can be provided:
369  * url: iframe url to load
370  * message: instead of a url to open, you could pass a message. HTML tags allowed.
371  * id: id attribute of modal window
372  * title: optional modal window title
373  * size: '
default', 'full'
374  * close: optional function to execute
on closing the modal
375  * footer: optional array of objects describing the buttons to display
in the footer.
376  * Each button
object can have the following members:
377  * label:
string, label of button
378  * bs_class:
string, button bootstrap class. Can be 'primary', 'default', 'success', 'warning' or 'danger'
379  * click: function to execute
on clicking the button. If the button closes the modal, this
380  * function
is executed before the close handler
381  * causes_closing: boolean,
default is true.
382  */

383 function modal_window(options){
384     
var id = options.id;
385     
var url = options.url;
386     
var title = options.title;
387     
var footer = options.footer;
388     
var message = options.message;
389
390     
if(typeof(id) == 'undefined') id = random_string(20);
391     
if(typeof(footer) == 'undefined') footer = [];
392
393     
if(jQuery('#' + id).length){
394         
/* modal exists -- remove it first */
395         jQuery('#' + id).
remove();
396     }
397
398     
/* prepare footer buttons, if any */
399     
var footer_buttons = '';
400     
for(i = 0; i < footer.length; i++){
401         
if(typeof(footer[i].causes_closing) == 'undefined'){ footer[i].causes_closing = true; }
402         
if(typeof(footer[i].bs_class) == 'undefined'){ footer[i].bs_class = 'default'; }
403         footer[i].id = id + '_footer_button_' + random_string(
10);
404
405         footer_buttons += '<button type="
button" class="btn btn-' + footer[i].bs_class + '" ' +
406                 (footer[i].causes_closing ? 'data-dismiss="
modal" ' : '') +
407                 'id="' + footer[i].id + '" ' +
408                 '>' + footer[i].label + '</button>';
409     }
410
411     jQuery('body').append(
412         '<div
class="modal fade" id="' + id + '" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">' +
413             '<div
class="modal-dialog">' +
414                 '<div
class="modal-content">' +
415                     ( title != undefined ?
416                         '<div
class="modal-header">' +
417                             '<button type="
button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>' +
418                             '<h3
class="modal-title" id="myModalLabel">' + title + '</h3>' +
419                         '</div>'
420                         : ''
421                     ) +
422                     '<div
class="modal-body" style="-webkit-overflow-scrolling:touch !important; overflow-y: auto;">' +
423                         ( url != undefined ?
424                             '<iframe width="
100%" height="100%" sandbox="allow-modals allow-forms allow-scripts allow-same-origin allow-popups" src="' + url + '"></iframe>'
425                             : message
426                         ) +
427                     '</div>' +
428                     ( footer != undefined ?
429                         '<div
class="modal-footer">' + footer_buttons + '</div>'
430                         : ''
431                     ) +
432                 '</div>' +
433             '</div>' +
434         '</div>'
435     );
436
437     
for(i = 0; i < footer.length; i++){
438         
if(typeof(footer[i].click) == 'function'){
439             jQuery('#' + footer[i].id).click(footer[i].click);
440         }
441     }
442
443     jQuery('#' + id).modal();
444
445     
if(typeof(options.close) == 'function'){
446         jQuery('#' + id).
on('hidden.bs.modal', options.close);
447     }
448
449     
if(typeof(options.size) == 'undefined') options.size = 'default';
450
451     
if(options.size == 'full'){
452         jQuery(window).resize(function(){
453             jQuery('#' + id + ' .modal-dialog').width(jQuery(window).width() *
0.95);
454             jQuery('#' + id + ' .modal-body').height(jQuery(window).height() *
0.7);
455         }).trigger('resize');
456     }
457
458     
return id;
459 }
460
461 function random_string(string_length){
462     
var text = "";
463     
var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
464
465     
for(var i = 0; i < string_length; i++)
466         text += possible.charAt(Math.floor(Math.random() * possible.length));
467
468     
return text;
469 }
470
471 function get_selected_records_ids(){
472     
return jQuery('.record_selector:checked').map(function(){ return jQuery(this).val() }).get();
473 }
474
475 function print_multiple_dv_tvdv(t, ids){
476     document.myform.NoDV.
value=1;
477     document.myform.PrintDV.
value=1;
478     document.myform.SelectedID.
value = '';
479     document.myform.submit();
480     
return true;
481 }
482
483 function print_multiple_dv_sdv(t, ids){
484     document.myform.NoDV.
value=1;
485     document.myform.PrintDV.
value=1;
486     document.myform.writeAttribute('novalidate', 'novalidate');
487     document.myform.submit();
488     
return true;
489 }
490
491 function mass_delete(t, ids){
492     
if(ids == undefined) return;
493     
if(!ids.length) return;
494
495     
var confirm_message = '<div class="alert alert-danger">' +
496             '<i
class="glyphicon glyphicon-warning-sign"></i> ' +
497             '<?php echo addslashes($Translation['<n> records will be deleted. Are you sure you want to
do this?']); ?>' +
498         '</div>';
499     
var confirm_title = '<?php echo addslashes($Translation['Confirm deleting multiple records']); ?>';
500     
var label_yes = '<?php echo addslashes($Translation['Yes, delete them!']); ?>';
501     
var label_no = '<?php echo addslashes($Translation['No, keep them.']); ?>';
502     
var progress = '<?php echo addslashes($Translation['Deleting record <i> of <n>']); ?>';
503     
var continue_delete = true;
504
505     
// request confirmation of mass delete operation
506     modal_window({
507         message: confirm_message.replace(/\<n\>/, ids.length),
508         title: confirm_title,
509         footer: [
/* shows a 'yes' and a 'no' buttons .. handler for each follows ... */
510             {
511                 label: '<i
class="glyphicon glyphicon-trash"></i> ' + label_yes,
512                 bs_class: 'danger',
513                 
// on confirming, start delete operations
514                 click: function(){
515
516                     
// show delete progress, allowing user to abort operations by closing the window or clicking cancel
517                     
var progress_window = modal_window({
518                         title: '<?php echo addslashes($Translation['Delete progress']); ?>',
519                         message: '' +
520                             '<div
class="progress">' +
521                                 '<div
class="progress-bar progress-bar-warning" role="progressbar" style="width: 0;"></div>' +
522                             '</div>' +
523                             '<button type="
button" class="btn btn-default details_toggle" onclick="' +
524                                 
'jQuery(this).children(\'.glyphicon\').toggleClass(\'glyphicon-chevron-right glyphicon-chevron-down\'); ' +
525                                 
'jQuery(\'.well.details_list\').toggleClass(\'hidden\');'
526                                 +
'">' +
527                                 
'<i class="glyphicon glyphicon-chevron-right"></i> ' +
528                                 
'<?php echo addslashes($Translation['Show/hide details']); ?>' +
529                             
'</button>' +
530                             
'<div class="well well-sm details_list hidden"><ol></ol></div>',
531                         close: function(){
532                             
// stop deleting further records ...
533                             continue_delete =
false;
534                         },
535                         footer: [
536                             {
537                                 label:
'<i class="glyphicon glyphicon-remove"></i> <?php echo addslashes($Translation['Cancel']); ?>',
538                                 bs_class:
'warning'
539                             }
540                         ]
541                     });
542
543                     
// begin deleting records, one by one
544                     progress = progress.replace(/\<n\>/, ids.length);
545                     
var delete_record = function(itrn){
546                         
if(!continue_delete) return;
547                         jQuery.ajax(t +
'_view.php', {
548                             type:
'POST',
549                             data: { delete_x:
1, SelectedID: ids[itrn] },
550                             success: function(resp){
551                                 
if(resp == 'OK'){
552                                     jQuery(
".well.details_list ol").append('<li class="text-success"><?php echo addslashes($Translation['The record has been deleted successfully']); ?></li>');
553                                     jQuery(
'#record_selector_' + ids[itrn]).prop('checked', false).parent().parent().fadeOut(1500);
554                                     jQuery(
'#select_all_records').prop('checked', false);
555                                 }
else{
556                                     jQuery(
".well.details_list ol").append('<li class="text-danger">' + resp + '</li>');
557                                 }
558                             },
559                             error: function(){
560                                 jQuery(
".well.details_list ol").append('<li class="text-warning"><?php echo addslashes($Translation['Connection error']); ?></li>');
561                             },
562                             complete: function(){
563                                 jQuery(
'#' + progress_window + ' .progress-bar').attr('style', 'width: ' + (Math.round((itrn + 1) / ids.length * 100)) + '%;').html(progress.replace(/\<i\>/, (itrn + 1)));
564                                 
if(itrn < (ids.length - 1)){
565                                     delete_record(itrn +
1);
566                                 }
else{
567                                     
if(jQuery('.well.details_list li.text-danger, .well.details_list li.text-warning').length){
568                                         jQuery(
'button.details_toggle').removeClass('btn-default').addClass('btn-warning').click();
569                                         jQuery(
'.btn-warning[id^=' + progress_window + '_footer_button_]')
570                                             .toggleClass(
'btn-warning btn-default')
571                                             .html(
'<?php echo addslashes($Translation['ok']); ?>');
572                                     }
else{
573                                         setTimeout(function(){ jQuery(
'#' + progress_window).modal('hide'); }, 500);
574                                     }
575                                 }
576                             }
577                         });
578                     }
579
580                     delete_record(
0);
581                 }
582             },
583             {
584                 label:
'<i class="glyphicon glyphicon-ok"></i> ' + label_no,
585                 bs_class:
'success'
586             }
587         ]
588     });
589 }
590
591 function mass_change_owner(t, ids){
592     
if(ids == undefined) return;
593     
if(!ids.length) return;
594
595     
var update_form = '<?php echo addslashes($Translation['Change owner of <n> selected records to']); ?> ' +
596         
'<span id="new_owner_for_selected_records"></span><input type="hidden" name="new_owner_for_selected_records" value="">';
597     
var confirm_title = '<?php echo addslashes($Translation['Change owner']); ?>';
598     
var label_yes = '<?php echo addslashes($Translation['Continue']); ?>';
599     
var label_no = '<?php echo addslashes($Translation['Cancel']); ?>';
600     
var progress = '<?php echo addslashes($Translation['Updating record <i> of <n>']); ?>';
601     
var continue_updating = true;
602
603     
// request confirmation of mass update operation
604     modal_window({
605         message: update_form.replace(/\<n\>/, ids.length),
606         title: confirm_title,
607         footer: [
/* shows a 'continue' and a 'cancel' buttons .. handler for each follows ... */
608             {
609                 label:
'<i class="glyphicon glyphicon-ok"></i> ' + label_yes,
610                 bs_class:
'success',
611                 
// on confirming, start update operations
612                 click: function(){
613                     
var memberID = jQuery('input[name=new_owner_for_selected_records]').eq(0).val();
614                     
if(!memberID.length) return;
615
616                     
// show update progress, allowing user to abort operations by closing the window or clicking cancel
617                     
var progress_window = modal_window({
618                         title:
'<?php echo addslashes($Translation['Update progress']); ?>',
619                         message:
'' +
620                             
'<div class="progress">' +
621                                 
'<div class="progress-bar progress-bar-success" role="progressbar" style="width: 0;"></div>' +
622                             
'</div>' +
623                             
'<button type="button" class="btn btn-default details_toggle" onclick="' +
624                                 
'jQuery(this).children(\'.glyphicon\').toggleClass(\'glyphicon-chevron-right glyphicon-chevron-down\'); ' +
625                                 
'jQuery(\'.well.details_list\').toggleClass(\'hidden\');'
626                                 +
'">' +
627                                 
'<i class="glyphicon glyphicon-chevron-right"></i> ' +
628                                 
'<?php echo addslashes($Translation['Show/hide details']); ?>' +
629                             
'</button>' +
630                             
'<div class="well well-sm details_list hidden"><ol></ol></div>',
631                         close: function(){
632                             
// stop updating further records ...
633                             continue_updating =
false;
634                         },
635                         footer: [
636                             {
637                                 label:
'<i class="glyphicon glyphicon-remove"></i> <?php echo addslashes($Translation['Cancel']); ?>',
638                                 bs_class:
'warning'
639                             }
640                         ]
641                     });
642
643                     
// begin updating records, one by one
644                     progress = progress.replace(/\<n\>/, ids.length);
645                     
var update_record = function(itrn){
646                         
if(!continue_updating) return;
647                         jQuery.ajax(
'admin/pageEditOwnership.php', {
648                             type:
'POST',
649                             data: {
650                                 pkValue: ids[itrn],
651                                 t: t,
652                                 memberID: memberID,
653                                 saveChanges:
'Save changes'
654                             },
655                             success: function(resp){
656                                 
if(resp == 'OK'){
657                                     jQuery(
".well.details_list ol").append('<li class="text-success"><?php echo addslashes($Translation['record updated']); ?></li>');
658                                     jQuery(
'#record_selector_' + ids[itrn]).prop('checked', false);
659                                     jQuery(
'#select_all_records').prop('checked', false);
660                                 }
else{
661                                     jQuery(
".well.details_list ol").append('<li class="text-danger">' + resp + '</li>');
662                                 }
663                             },
664                             error: function(){
665                                 jQuery(
".well.details_list ol").append('<li class="text-warning"><?php echo addslashes($Translation['Connection error']); ?></li>');
666                             },
667                             complete: function(){
668                                 jQuery(
'#' + progress_window + ' .progress-bar').attr('style', 'width: ' + (Math.round((itrn + 1) / ids.length * 100)) + '%;').html(progress.replace(/\<i\>/, (itrn + 1)));
669                                 
if(itrn < (ids.length - 1)){
670                                     update_record(itrn +
1);
671                                 }
else{
672                                     
if(jQuery('.well.details_list li.text-danger, .well.details_list li.text-warning').length){
673                                         jQuery(
'button.details_toggle').removeClass('btn-default').addClass('btn-warning').click();
674                                         jQuery(
'.btn-warning[id^=' + progress_window + '_footer_button_]')
675                                             .toggleClass(
'btn-warning btn-default')
676                                             .html(
'<?php echo addslashes($Translation['ok']); ?>');
677                                     }
else{
678                                         jQuery(
'button.btn-warning[id^=' + progress_window + '_footer_button_]')
679                                             .toggleClass(
'btn-warning btn-success')
680                                             .html(
'<i class="glyphicon glyphicon-ok"></i> <?php echo addslashes($Translation['ok']); ?>');
681                                     }
682                                 }
683                             }
684                         });
685                     }
686
687                     update_record(
0);
688                 }
689             },
690             {
691                 label:
'<i class="glyphicon glyphicon-remove"></i> ' + label_no,
692                 bs_class:
'warning'
693             }
694         ]
695     });
696
697     
/* show drop down of users */
698     
var populate_new_owner_dropdown = function(){
699
700         jQuery(
'[id=new_owner_for_selected_records]').select2({
701             width:
'100%',
702             formatNoMatches: function(term){
return '<?php echo addslashes($Translation['No matches found!']); ?>'; },
703             minimumResultsForSearch:
10,
704             loadMorePadding:
200,
705             escapeMarkup: function(m){
return m; },
706             ajax: {
707                 url:
'admin/getUsers.php',
708                 dataType:
'json',
709                 cache:
true,
710                 data: function(term, page){
return { s: term, p: page, t: t }; },
711                 results: function(resp, page){
return resp; }
712             }
713         }).
on('change', function(e){
714             jQuery(
'[name="new_owner_for_selected_records"]').val(e.added.id);
715         });
716
717     }
718
719     populate_new_owner_dropdown();
720 }
721
722 function add_more_actions_link(){
723     window.open(
'https://bigprof.com/appgini/help/advanced-topics/hooks/multiple-record-batch-actions?r=appgini-action-menu');
724 }

725
726 /* detect current screen size (xs, sm, md or lg) */

727 function screen_size(sz){
728     
if(!$j('.device-xs').length){
729         $j(
'body').append(
730             
'<div class="device-xs visible-xs"></div>' +
731             
'<div class="device-sm visible-sm"></div>' +
732             
'<div class="device-md visible-md"></div>' +
733             
'<div class="device-lg visible-lg"></div>'
734         );
735     }
736     
return $j('.device-' + sz).is(':visible');
737 }

738
739 /* enable floating of action buttons
in DV so they are visible on vertical scrolling */
740 function enable_dvab_floating(){
741     
/* already run? */
742     
if(window.enable_dvab_floating_run != undefined) return;
743
744     
/* scroll action buttons of DV on scrolling DV */
745     $j(window).scroll(function(){
746         
if(!screen_size('md') && !screen_size('lg')) return;
747         
if(!$j('.detail_view').length) return;
748
749         
/* get vscroll amount, DV form height, button toolbar height and position */
750         
var vscroll = $j(window).scrollTop();
751         
var dv_height = $j('[id$="_dv_form"]').eq(0).height();
752         
var bt_height = $j('.detail_view .btn-toolbar').height();
753         
var form_top = $j('.detail_view .form-group').eq(0).offset().top;
754         
var bt_top_max = dv_height - bt_height - 10;
755
756         
if(vscroll > form_top){
757             
var tm = parseInt(vscroll - form_top) + 60;
758             
if(tm > bt_top_max) tm = bt_top_max;
759
760             $j(
'.detail_view .btn-toolbar').css({ 'margin-top': tm + 'px' });
761         }
else{
762             $j(
'.detail_view .btn-toolbar').css({ 'margin-top': 0 });
763         }
764     });
765     window.enable_dvab_floating_run =
true;
766 }

767
768 /* check
if a given field's value is unique and reflect this in the DV form */
769 function enforce_uniqueness(table, field){
770     $j(
'#' + field).on('change', function(){
771         
/* check uniqueness of field */
772         
var data = {
773             t: table,
774             f: field,
775             
value: $j('#' + field).val()
776         };
777
778         
if($j('[name=SelectedID]').val().length) data.id = $j('[name=SelectedID]').val();
779
780         $j.ajax({
781             url:
'ajax_check_unique.php',
782             data: data,
783             complete: function(resp){
784                 
if(resp.responseJSON.result == 'ok'){
785                     $j(
'#' + field + '-uniqueness-note').hide();
786                     $j(
'#' + field).parents('.form-group').removeClass('has-error');
787                 }
else{
788                     $j(
'#' + field + '-uniqueness-note').show();
789                     $j(
'#' + field).parents('.form-group').addClass('has-error');
790                     $j(
'#' + field).focus();
791                     setTimeout(function(){ $j(
'#update, #insert').prop('disabled', true); }, 500);
792                 }
793             }
794         })
795     });
796 }

797
798 /* persist expanded/collapsed chidren
in DVP */
799 function persist_expanded_child(id){
800     
var expand_these = Cookies.getJSON('online_clinic_management_system.dvp_expand');
801     
if(expand_these == undefined) expand_these = [];
802
803     
if($j('[id=' + id + ']').hasClass('active')){
804         
if(expand_these.indexOf(id) < 0){
805             
// expanded button and not persisting in cookie? save it!
806             expand_these.push(id);
807             Cookies.
set('online_clinic_management_system.dvp_expand', expand_these, { expires: 30 });
808         }
809     }
else{
810         
if(expand_these.indexOf(id) >= 0){
811             
// collapsed button and persisting in cookie? remove it!
812             expand_these.splice(expand_these.indexOf(id),
1);
813             Cookies.
set('online_clinic_management_system.dvp_expand', expand_these, { expires: 30 });
814         }
815     }
816 }

817
818 /* apply expanded/collapsed status to children
in DVP */
819 function apply_persisting_children(){
820     
var expand_these = Cookies.getJSON('online_clinic_management_system.dvp_expand');
821     
if(expand_these == undefined) return;
822
823     expand_these.each(function(id){
824         $j(
'[id=' + id + ']:not(.active)').click();
825     });
826 }
827
828 function select2_max_width_decrement(){
829     
return ($j('div.container').eq(0).hasClass('theme-compact') ? 99 : 109);
830 }


Gõ tìm kiếm nhanh...